package com.crissy.messageService.utils;


import cn.hutool.core.util.HexUtil;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

public class AESUtils {
    private static final String ALGORITHM = "AES";
    private static final String UTF8 = StandardCharsets.UTF_8.name();

    /**
     * 根据指定的类型和种子字符串获取Cipher实例。
     * 该方法可用于初始化Cipher为加密或解密操作。
     *
     * @param type Cipher的初始化模式，如Cipher.ENCRYPT_MODE或Cipher.DECRYPT_MODE。
     * @param seed 用于生成对称密钥的种子字符串。
     * @return 根据指定类型和种子初始化后的Cipher实例。
     * @throws NoSuchPaddingException       如果指定的填充方式不存在。
     * @throws NoSuchAlgorithmException     如果指定的算法不存在。
     * @throws UnsupportedEncodingException 如果指定的编码格式不支持。
     * @throws InvalidKeyException          如果密钥无效。
     */
    public static Cipher getCipher(int type, String seed) throws Exception {
        // 获取Cipher实例，使用预定义的算法。
        Cipher cipher = Cipher.getInstance(ALGORITHM);

        // 初始化密钥生成器并使用种子生成对称密钥。
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
        SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
        secureRandom.setSeed(seed.getBytes(UTF8)); // 使用种子设置随机数生成器。
        keyGenerator.init(128, secureRandom); // 初始化密钥生成器。

        SecretKey secretKey = keyGenerator.generateKey(); // 生成密钥。

        byte[] encoded = secretKey.getEncoded(); // 获取密钥的编码形式。
        // 使用种子字符串创建对称密钥。
        SecretKeySpec secretKeySpec = new SecretKeySpec(encoded, ALGORITHM);

        // 根据类型初始化Cipher，准备进行加密或解密操作。
        cipher.init(type, secretKeySpec);

        return cipher;
    }

    /**
     * 使用指定的密钥对给定的数据进行加密。
     *
     * @param key         加密/解密算法的密钥，为字符串形式。该密钥用于执行加密操作。
     * @param encryptData 需要被加密的数据，为字符串形式。待加密的数据将以此形式输入。
     * @return 加密后的数据，以Base64编码的字符串形式返回。返回的数据是经过加密并编码后的结果，便于存储或传输。
     * @throws NoSuchPaddingException       如果请求的填充模式不可用。这通常意味着所使用的加密算法不支持所需的填充模式。
     * @throws NoSuchAlgorithmException     如果请求的算法不可用。这可能是因为加密算法未在系统中注册或不可用。
     * @throws UnsupportedEncodingException 如果指定的编码不被支持。此处特指UTF-8编码不被支持的情况。
     * @throws InvalidKeyException          如果提供的密钥不合法。密钥可能不符合要求的格式或长度。
     * @throws IllegalBlockSizeException    如果加密操作的输入数据块大小不合法。这可能是因为数据块大小超出了加密算法的限制。
     * @throws BadPaddingException          如果加密数据的末尾填充不合法。这可能是因为数据在加密过程中出现了意外的填充模式。
     */
    public static String encrypt(String key, String encryptData) throws Exception {
        // 初始化加密器，准备执行加密操作
        Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, key);
        // 将需要加密的数据转换为字节数组，然后执行加密操作
        byte[] encodedBytes = cipher.doFinal(encryptData.getBytes(UTF8));
        // 将加密后的字节数据编码为Base64字符串返回，以便于在不丢失信息的情况下进行传输或存储
        return HexUtil.encodeHexStr(encodedBytes);
    }


    /**
     * 使用指定的密钥解密数据。
     *
     * @param key         密钥，用于解密数据。
     * @param decryptData 待解密的数据，以十六进制字符串形式提供。
     * @return 解密后的字符串。
     * @throws Exception 如果解密过程中发生错误，则抛出异常。
     */
    public static String decrypt(String key, String decryptData) throws Exception {
        // 将待解密数据从十六进制字符串转换为字节数组
        byte[] bytes = HexUtil.decodeHex(decryptData);
        // 获取解密用的Cipher实例
        Cipher cipher = getCipher(Cipher.DECRYPT_MODE, key);
        // 使用密钥和Cipher解密数据
        byte[] decodedBytes = cipher.doFinal(bytes);
        // 将解密后的字节数组转换为字符串并返回
        return new String(decodedBytes, UTF8);
    }

}